package collections.list.array_list;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;

import java.util.Iterator;
import java.util.NoSuchElementException;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class MyArrayListTestsForInteger {

	MyArrayList<Integer> list;
	public static final int TEST_VALUE_0 = 0;
	public static final int TEST_VALUE_1 = 1;
	public static final int TEST_VALUE_2 = 2;
	public static final int TEST_VALUE_3 = 3;

	@Before
	public void initCollection() {
		list = new MyArrayList<>();
		list.add(TEST_VALUE_0);
		list.add(TEST_VALUE_1);
		list.add(TEST_VALUE_2);
		list.add(TEST_VALUE_3);
	}

	@Test
	public void should_change_size_after_adding_new_element() {
		Assert.assertThat(list.size(), equalTo(4));
		list.add(4);
		Assert.assertThat(list.size(), equalTo(5));
	}

	@Test
	public void should_change_size_after_adding_allready_present_element() {
		Assert.assertThat(list.size(), equalTo(4));
		list.add(TEST_VALUE_0);
		Assert.assertThat(list.size(), equalTo(5));
	}

	@Test
	public void should_change_size_after_removing_element() {
		Assert.assertThat(list.size(), equalTo(4));
		list.remove(0);
		Assert.assertThat(list.size(), equalTo(3));
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_3));
	}

	@Test
	public void should_add_to_the_end_of_the_list_if_index_not_entered() {
		list.add(4);
		Assert.assertThat(list.get(4), equalTo(4));
	}

	@Test
	public void should_add_to_position_entered_as_index_value() {
		list.add(0, 4);
		Assert.assertThat(list.get(0), equalTo(4));
	}

	@Test
	public void should_removing_element_on_entered_possition() {
		list.remove(0);
		Assert.assertThat(list.get(0), not(equalTo(TEST_VALUE_0)));
	}

	@Test
	public void all_elements_should_change_thare_positions_after_adding_new_element_to_the_start_of_the_list() {
		list.add(0, 4);
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_0));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(3), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(4), equalTo(TEST_VALUE_3));
	}

	@Test
	public void right_situated_elements_should_change_thare_positions_after_adding_new_element_to_the_middle_of_the_list() {
		list.add(2, 4);
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_0));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(3), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(4), equalTo(TEST_VALUE_3));
	}

	@Test
	public void elements_should_not_change_thare_positions_after_adding_new_element_to_the_end_of_the_list() {
		list.add(4);
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_0));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(3), equalTo(TEST_VALUE_3));
	}

	@Test
	public void all_elements_should_change_thare_positions_after_removing_element_from_the_start_of_the_list() {
		list.remove(0);
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_3));
	}

	@Test
	public void right_situated_elements_should_change_thare_positions_after_removing_element_from_the_middle_of_the_list() {
		list.remove(2);
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_0));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_3));
	}

	@Test
	public void elements_should_not_change_thare_positions_after_removing_element_from_the_end_of_the_list() {
		list.remove(3);
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_0));
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_2));
	}

	@Test
	public void should_add_an_array_of_elements_to_the_end_of_the_list_if_index_not_entered() {
		list.addAll(new Integer[] { 4, 5 });
		Assert.assertThat(list.get(4), equalTo(4));
		Assert.assertThat(list.get(5), equalTo(5));
	}

	@Test
	public void should_add_an_array_of_elements_to_the_position_entered_as_a_index() {
		list.AddAll(2, new Integer[] { 4, 5 });
		Assert.assertThat(list.get(2), equalTo(4));
		Assert.assertThat(list.get(3), equalTo(5));
	}

	@Test
	public void should_get_element_in_the_entered_position() {
		Assert.assertThat(list.get(0), equalTo(TEST_VALUE_0));
	}

	@Test
	public void should_clear_list() {
		list.clear();
		Assert.assertThat(list.size(), equalTo(0));
	}

	@Test
	public void isEmpty_should_return_true_if_list_is_empty() {
		list.clear();
		Assert.assertThat(list.isEmpty(), equalTo(true));
	}

	@Test
	public void isEmpty_should_return_false_if_list_is_not_empty() {
		Assert.assertThat(list.isEmpty(), equalTo(false));
	}

	@Test
	public void should_set_value_to_entered_possition() {
		list.set(0, 4);
		Assert.assertThat(list.get(0), equalTo(4));
	}

	@Test
	public void size_after_setting_new_element_should_not_change() {
		list.set(0, 4);
		Assert.assertThat(list.size(), equalTo(4));
	}

	@Test
	public void rest_element_should_stay_on_the_same_place_after_setting_new_element() {
		list.set(0, 4);
		Assert.assertThat(list.get(1), equalTo(TEST_VALUE_1));
		Assert.assertThat(list.get(2), equalTo(TEST_VALUE_2));
		Assert.assertThat(list.get(3), equalTo(TEST_VALUE_3));
	}

	@Test
	public void indexOf_should_return_index_of_entered_element() {
		Assert.assertThat(list.indexOf(TEST_VALUE_0), equalTo(0));
		Assert.assertThat(list.indexOf(TEST_VALUE_1), equalTo(1));
		Assert.assertThat(list.indexOf(TEST_VALUE_2), equalTo(2));
		Assert.assertThat(list.indexOf(TEST_VALUE_3), equalTo(3));
	}

	@Test
	public void toArray_should_return_array_with_elements_on_current_list(){
		Integer[] array = list.toArray();
		Assert.assertThat(array.length, equalTo(4));
		Assert.assertThat(array[0], equalTo(TEST_VALUE_0));
		Assert.assertThat(array[1], equalTo(TEST_VALUE_1));
		Assert.assertThat(array[2], equalTo(TEST_VALUE_2));
		Assert.assertThat(array[3], equalTo(TEST_VALUE_3));
	}

	@Test(expected = IndexOutOfBoundsException.class)
	public void add_method_should_throw_IndexOutOfBoundException_if_size_of_list_to_small(){
		list.add(10, 10);
	}
	
	@Test(expected = IndexOutOfBoundsException.class)
	public void addAll_method_should_throw_IndexOutOfBoundException_if_size_of_list_to_small(){
		list.AddAll(10, new Integer[]{1, 2, 3});
	}
	
	@Test(expected = IndexOutOfBoundsException.class)
	public void get_method_should_throw_IndexOutOfBoundException_if_size_of_list_to_small(){
		list.get(10);
	}
	
	@Test(expected = IndexOutOfBoundsException.class)
	public void remove_method_should_throw_IndexOutOfBoundException_if_size_of_list_to_small(){
		list.remove(10);
	}
	
	@Test(expected = IndexOutOfBoundsException.class)
	public void set_method_should_throw_IndexOutOfBoundException_if_size_of_list_to_small(){
		list.set(10, 10);
	}
	
	@Test(expected = NoSuchElementException.class)
	public void indexOf_should_throw_exception_if_there_is_no_such_element_in_current_list(){
		list.indexOf(10);
	}
	
	@Test
	public void iterator_should_iterate_thrue_all_elements_of_the_list() {
		Iterator<Integer> iterator = list.iterator();
		Assert.assertThat(iterator.hasNext(), equalTo(true));
		Assert.assertThat(iterator.next(), equalTo(0));
		Assert.assertThat(iterator.hasNext(), equalTo(true));
		Assert.assertThat(iterator.next(), equalTo(1));
		Assert.assertThat(iterator.hasNext(), equalTo(true));
		Assert.assertThat(iterator.next(), equalTo(2));
		Assert.assertThat(iterator.hasNext(), equalTo(true));
		Assert.assertThat(iterator.next(), equalTo(3));
		Assert.assertThat(iterator.hasNext(), equalTo(false));
	}
}
